#include "Table.h"

Table::Table() {

}

Table::Table(std::string table_name, std::string fields_set) {
    this->name = table_name;
    this->field_count = 0;
    this->record_count = 0;

    std::vector<std::string> words;
    std::string t = "";
    for (auto c : fields_set) {
        if (c == ' ') {
            words.push_back(t);
            t = "";
        } else if (c == ',') {
            // skip
        } else {
            t += c;
        }
    }
    words.push_back(t);

    for (int i = 0; i < words.size(); i += 2) {
        Field fd;

        if (words[i] == "int") fd.type = 0;
        else if (words[i] == "float") fd.type = 1;
        else fd.type = 2;

        fd.name = words[i+1];

        this->fields.push_back(fd);
        this->field_count++;
    }
}

void Table::info() {
    std::cout << "Table: \"" << this->name << "\":" << std::endl;
    std::cout << "Total " << this->field_count << " fields:" << std::endl;
    for (auto fd : this->fields) {
        std::cout << "    - ";
        if (fd.type == 0) std::cout << "int ";
        else if (fd.type == 1) std::cout << "float ";
        else std::cout << "string ";
        std::cout << fd.name << std::endl;
    }
    std::cout << "Total " << this->record_count << " records." << std::endl;
}

void Table::add_record(std::string fields) {
    // start adding every field of the record to this->fields
    std::vector<std::string> words;

    std::string t;
    for (auto c : fields) {
        if (c == '=' || c == ',') {
            words.push_back(t);
            t = "";
        } else if (c == '\'' || c == ' ') {
            // skip
        } else {
            t += c;
        }
    }
    words.push_back(t);

    // words = ["id", "210001", "name", "liming", "math", "90", "cs", "92.5", "english", "87"]

    if (words.size() % 2 != 0) {
        std::cout << "ERROR: invalid fields" << std::endl;
        return;
    }

    std::vector<std::string> field_names;

    // check
    for (int i = 0; i < words.size(); i += 2) {
        field_names.push_back(words[i]);
    }

    for (int i = 0; i < this->field_count; i++) {
        if (std::find(field_names.begin(), field_names.end(), this->fields[i].name) == field_names.end()) {
            std::cout << "ERROR: field " << this->fields[i].name << " not set" << std::endl;
            return;
        }
    }

    // set
    for (int i = 0; i < words.size(); i += 2) {
        // IMPORTANT!!! Must use iterator!!!
        for (auto fd = this->fields.begin(); fd != this->fields.end(); fd++) {
            if (fd->name == words[i]) {
                fd->add_item(words[i+1]);
            }
        }
    }

    this->record_count++;
}

void Table::delete_record(std::string filter) {
    // filter = "id='210001'"
    // filter = "math<60"
    if (filter == "all") {
        for (auto i = this->fields.begin(); i != this->fields.end(); i++) {
            i->records_int.clear();
            i->records_float.clear();
            i->records_string.clear();
        }
        this->record_count = 0;
        return;
    } else {
        std::string field_name = ""; // "id"
        char op; // '='
        std::string val = ""; // "210001"

        // read into field_name, op and val
        int i = 0;
        while (filter[i] != '=' && filter[i] != '<' && filter[i] != '>') {
            field_name += filter[i];
            i++;
        }

        op = filter[i++];

        while (i < filter.size()) {
            if (filter[i] != '\'') val += filter[i];
            i++;
        }

        // search for the right field and right record
        for (auto fd = this->fields.begin(); fd != this->fields.end(); fd++) {
            if (fd->name == field_name) {
                std::vector<int> to_be_deleted; // index of records

                // search for records (that match the filter) to be deleted
                for (int j = 0; j < this->record_count; j++) {
                    if (fd->match(j, op, val)) to_be_deleted.push_back(j);
                }

                // delete them
                // 从后往前删，不然会出错
                std::reverse(to_be_deleted.begin(), to_be_deleted.end());

                for (auto i = this->fields.begin(); i != this->fields.end(); i++) {
                    for (int j : to_be_deleted) {
                        i->delete_item(j);
                    }
                }

                this->record_count -= to_be_deleted.size();

                return;
            }
        }

        std::cout << "ERROR: no field named \"" << field_name << "\" found" << std::endl;
        return;
    }
}

void Table::select(std::string show_fields, std::string filters, std::string order) {
    // show_fields = "*" or "id, name, math"
    // filters = "math<60 english<60" or "*"
    // order = "math desc" or "none"

    // 1. get fields to show
    std::vector<std::string> fields;
    std::string tmp = "";
    for (auto c : show_fields) {
        if (c == ',') {
            fields.push_back(tmp);
            tmp = "";
        } else if (c == ' ') {
            // skip
        } else {
            tmp += c;
        }
    }
    fields.push_back(tmp);
    tmp = "";

    // 2. get records to show
    std::vector<int> show_records(this->record_count, 1); // 1--show 0--don't show
    
    std::vector<std::string> conditions;
    for (auto c : filters) {
        if (c == ' ') {
            conditions.push_back(tmp);
            tmp = "";
        } else {
            tmp += c;
        }
    }
    conditions.push_back(tmp);
    tmp = "";

    // conditions = ["math<60", "english<60"]

    for (auto condition : conditions) {
        if (condition == "all") {
            break;
        }

        std::string field_name = ""; // "id"
        char op; // '='
        std::string val = ""; // "210001"

        // read into field_name, op and val
        int i = 0;
        while (condition[i] != '=' && condition[i] != '<' && condition[i] != '>') {
            field_name += condition[i];
            i++;
        }

        op = condition[i++];

        while (i < condition.size()) {
            if (condition[i] != '\'') val += condition[i];
            i++;
        }

        // find the right field and right records

        for (auto fd = this->fields.begin(); fd != this->fields.end(); fd++) {
            if (fd->name == field_name) {

                // search for records (that match the filter) to be deleted
                for (int j = 0; j < this->record_count; j++) {
                    if (!fd->match(j, op, val)) {
                        show_records[j] = 0;
                    }
                }
            }
        }
    }

    std::vector<int> show_sequence;
    for (int i = 0; i < show_records.size(); i++) {
        if (show_records[i] == 1) {
            show_sequence.push_back(i);
        }
    }

    // 3. sort if there is an order

    if (order != "none") {
        std::string order_field; // "math"
        bool asc;   // true-asc, false-desc

        tmp = "";

        for (auto c : order) {
            if (c == ' ') {
                order_field = tmp;
                tmp = "";
            } else {
                tmp += c;
            }
        }
        if (tmp == "asc") asc = true;
        else asc = false;

        tmp = "";

        for (auto fd = this->fields.begin(); fd != this->fields.end(); fd++) {
            if (fd->name == order_field) {
                // operate on show_records
                if (fd->type == 0) {
                    for (int i = show_sequence.size()-2; i >= 0; i--) {
                        for (int j = 0; j <= i; j++) {
                            if (fd->records_int[show_sequence[j]] > fd->records_int[show_sequence[j+1]])
                                std::swap(show_sequence[j], show_sequence[j+1]);
                        }
                    }
                } else if (fd->type == 1) {
                    for (int i = show_sequence.size()-2; i >= 0; i--) {
                        for (int j = 0; j <= i; j++) {
                            if (fd->records_float[show_sequence[j]] > fd->records_float[show_sequence[j+1]])
                                std::swap(show_sequence[j], show_sequence[j+1]);
                        }
                    }
                }
            }
        }

        if (!asc) std::reverse(show_sequence.begin(), show_sequence.end());
    }

    // 4. show
    // using fields and show_sequence
    std::vector<Field*> field_pts;
    if (fields[0] == "*") {
        for (auto fd = this->fields.begin(); fd != this->fields.end(); fd++) {
            field_pts.push_back(&*fd);
        }
    } else {
        for (auto name : fields) {
            for (auto fd = this->fields.begin(); fd != this->fields.end(); fd++) {
                if (fd->name == name) {
                    field_pts.push_back(&*fd);
                }
            }
        }
    }
    
    std::cout << "         ";
    for (auto field_pt : field_pts) {
        std::cout << field_pt->name << " ";
    }

    std::cout << std::endl;

    for (auto id : show_sequence) {
        std::cout << "index " << id << ": ";
        for (auto field_pt : field_pts) {
            field_pt->print_item(id);
        }
        std::cout << std::endl;
    }
}
